package org.apache.catalina.startup;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.Set;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.JarScanType;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
import org.apache.tomcat.util.descriptor.web.WebXml;
import org.apache.tomcat.util.descriptor.web.WebXmlParser;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.xml.sax.InputSource;

import com.github.netty.core.util.ResourceManager;
import com.github.netty.protocol.servlet.ServletContext;

public class ServletContextConfig {
	private static final Log log = LogFactory.getLog(ServletContextConfig.class);
	/**
     * The string resources for this package.
     */
    private static final StringManager sm = StringManager.getManager(Constants.Package);
    
	private final ResourceManager resourceManager;
	private final ServletContext servletContext;
	private boolean ok;

	public ServletContextConfig(ServletContext servletContext) {
		this.servletContext = servletContext;
		this.resourceManager = servletContext.getResourceManager();
	}

	protected void webConfig() {
		if(resourceManager==null) {
			log.warn("resourceManager is null");
			return;
		}
		InputSource inputSource = getContextWebXmlSource();
		if(inputSource==null) {
			return;
		}
		WebXmlParser webXmlParser = new WebXmlParser(false, false,
                true);
		
		WebXml webXml = createWebXml();
		ok = webXmlParser.parseWebXml(inputSource, webXml, false);
		
		// Ordering is important here

        // Step 1. Identify all the JARs packaged with the application and those
        // provided by the container. If any of the application JARs have a
        // web-fragment.xml it will be parsed at this point. web-fragment.xml
        // files are ignored for container provided JARs.
        Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);

        // Step 2. Order the fragments.
        Set<WebXml> orderedFragments = WebXml.orderWebFragments(webXml, fragments, servletContext);
        System.out.println(orderedFragments);

	}
	
	private WebXml createWebXml() {
        return new WebXml();
    }
	
	private InputSource getContextWebXmlSource() {
		InputSource source = null;
		try (InputStream stream = resourceManager.getResourceAsStream(Constants.ApplicationWebXml);
				ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) {
			if (stream == null) {
				if (log.isDebugEnabled()) {
					log.debug("Missing application web.xml, using defaults only");
				}
				return source;
			}
			URL url = resourceManager.getResource(Constants.ApplicationWebXml);
			if (url == null) {
				if (log.isDebugEnabled()) {
					log.debug("Unable to determine URL for application web.xml");
				}
				return source;
			}
			byte[] buffer = new byte[8192];
		    int bytesRead;
		    while ((bytesRead = stream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
			source = new InputSource(url.toExternalForm());
			// Convert the byte array into a ByteArrayInputStream
	        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outputStream.toByteArray());
			source.setByteStream(byteArrayInputStream);
		} catch (Exception e) {
			log.error(e);
		}
		return source;
	}
	
	/**
     * Scan /WEB-INF/lib for JARs and for each one found add it and any /META-INF/web-fragment.xml to the resulting Map.
     * web-fragment.xml files will be parsed before being added to the map. Every JAR will be added and
     * <code>null</code> will be used if no web-fragment.xml was found. Any JARs known not contain fragments will be
     * skipped.
     *
     * @param application  The main web.xml metadata
     * @param webXmlParser The parser to use to process the web.xml file
     *
     * @return A map of JAR name to processed web fragment (if any)
     */
    private Map<String,WebXml> processJarsForWebFragments(WebXml application, WebXmlParser webXmlParser) {
        JarScanner jarScanner = new StandardJarScanner();
        boolean delegate = JreCompat.isGraalAvailable();
        
        boolean parseRequired = true;
        Set<String> absoluteOrder = application.getAbsoluteOrdering();
        if (absoluteOrder != null && absoluteOrder.isEmpty()) {
            // Skip parsing when there is an empty absolute ordering and
            // validation is not enabled
            parseRequired = false;
        }

        FragmentJarScannerCallback callback = new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);

        jarScanner.scan(JarScanType.PLUGGABILITY, servletContext, callback);

        if (!callback.isOk()) {
            ok = false;
        }
        return callback.getFragments();
    }

}
